import os
import json
from structs import Project, Plan
from agent import agent_project_maker,agent_testcase_generator,agent_code_completer
from codetest import execute_and_test_python_code
# 项目文件存储的基础路径
PROJECT_BASE_DIR = "project/"

def _get_project_dir_path(user_id: str, lang: str, direction: str, step: int) -> str:
    """根据用户信息和计划步骤获取项目目录路径"""
    dir_name = f"{user_id}_{lang}_{direction}"
    return os.path.join(PROJECT_BASE_DIR, dir_name, str(step))

def _get_specific_project_path(user_id: str, lang: str, direction: str, step: int, project_index: int) -> str:
    """获取特定项目子目录的路径"""
    base_dir = _get_project_dir_path(user_id, lang, direction, step)
    return os.path.join(base_dir, f"project_{project_index}")
'''上面的是内部使用函数'''
# 新建project使用的保存
def save_new_project(user_id: str, lang: str, direction: str, step: int, project_data: Project):
    """
    保存单个项目的数据到文件系统。

    Args:
        user_id: 用户ID。
        lang: 语言。
        direction: 方向。
        step: 计划的第几步。
        project_data: 包含代码、README、名称和答案的Project对象。
    """
    # --- 计算下一个 project_index ---
    project_dir_path = _get_project_dir_path(user_id, lang, direction, step)
    os.makedirs(project_dir_path, exist_ok=True) # 确保基础目录存在

    existing_indices = []
    if os.path.exists(project_dir_path) and os.path.isdir(project_dir_path):
        for item in os.listdir(project_dir_path):
            item_path = os.path.join(project_dir_path, item)
            if os.path.isdir(item_path) and item.startswith("project_"):
                try:
                    index_str = item.split('_')[-1]
                    existing_indices.append(int(index_str))
                except (ValueError, IndexError):
                    print(f"警告：无法从目录名 '{item}' 解析索引于 {project_dir_path}")

    if existing_indices:
        project_index = max(existing_indices) + 1
    else:
        project_index = 0 # 如果没有现有项目，从 0 开始

    print(f"为用户 {user_id} 在 {lang}/{direction}/step{step} 下自动分配 project_index: {project_index}")
    # --- 结束计算 project_index ---
    save_project(user_id, lang, direction, step, project_data, project_index)


def save_project(user_id: str, lang: str, direction: str, step: int, project_data: Project, project_index: int):
    project_path = _get_specific_project_path(user_id, lang, direction, step, project_index)
    os.makedirs(project_path, exist_ok=True)  # 确保特定项目目录存在

    # 保存代码
    with open(os.path.join(project_path, "code.py"), "w", encoding="utf-8") as f:
        f.write(project_data.code)

    # 保存README
    with open(os.path.join(project_path, "readme.md"), "w", encoding="utf-8") as f:
        f.write(project_data.md)

    # 保存项目名称到info文件
    with open(os.path.join(project_path, "info.txt"), "w", encoding="utf-8") as f:
        f.write(project_data.name)

    # 保存答案
    with open(os.path.join(project_path, "answer.py"), "w", encoding="utf-8") as f:
        f.write(project_data.answer)
 
def get_project_list(user_id: str, lang: str, direction: str, step: int) -> list[dict]:
    """
    获取指定计划步骤下的所有项目列表（包含ID和名称）。

    Args:
        user_id: 用户ID。
        lang: 语言。
        direction: 方向。
        step: 计划的第几步。

    Returns:
        一个包含项目字典的列表，每个字典包含 'id' 和 'name'。
        例如: [{'id': 'project_0', 'name': '项目名称0'}, ...]
    """
    project_dir_path = _get_project_dir_path(user_id, lang, direction, step)
    if not os.path.exists(project_dir_path) or not os.path.isdir(project_dir_path):
        return []

    projects = []
    for item in os.listdir(project_dir_path):
        item_path = os.path.join(project_dir_path, item)
        if os.path.isdir(item_path) and item.startswith("project_"):
            info_file_path = os.path.join(item_path, "info.txt")
            if os.path.isfile(info_file_path):
                try:
                    with open(info_file_path, "r", encoding="utf-8") as f:
                        project_name = f.read().strip()
                    # 使用目录名作为 id，读取的文件内容作为 name
                    projects.append({"id": item, "name": project_name})
                except Exception as e:
                    print(f"Error reading info file for project {item} in {project_dir_path}: {e}")
                    # 如果读取失败，可以选择跳过或添加一个带错误信息的条目
                    # projects.append({"id": item, "name": "Error reading name"})

    # 按项目索引（ID中的数字）排序
    projects.sort(key=lambda p: int(p['id'].split('_')[-1]))
    return projects


def get_project_content(user_id: str, lang: str, direction: str, step: int, project_index: int) -> Project | None:
    """
    获取特定项目的内容。

    Args:
        user_id: 用户ID。
        lang: 语言。
        direction: 方向。
        step: 计划的第几步。
        project_index: 项目的索引 (i)。

    Returns:
        包含项目代码、README和名称的Project对象，如果找不到则返回None。
    """
    project_path = _get_specific_project_path(user_id, lang, direction, step, project_index)

    code_path = os.path.join(project_path, "code.py")
    md_path = os.path.join(project_path, "readme.md")
    info_path = os.path.join(project_path, "info.txt")
    answer_path = os.path.join(project_path, "answer.py") # 新增 answer 文件路径

    # 检查所有必需文件是否存在
    if not all(os.path.exists(p) for p in [code_path, md_path, info_path, answer_path]):
        # 可以添加更详细的日志记录，说明哪个文件缺失
        print(f"Warning: Missing one or more project files in {project_path}")
        return None

    try:
        with open(code_path, "r", encoding="utf-8") as f:
            code = f.read()
        with open(md_path, "r", encoding="utf-8") as f:
            md = f.read()
        with open(info_path, "r", encoding="utf-8") as f:
            name = f.read().strip() # 读取名称并去除可能的前后空格/换行
        with open(answer_path, "r", encoding="utf-8") as f: # 读取 answer 文件
            answer = f.read()

        # 在创建 Project 实例时包含 answer
        return Project(code=code, md=md, name=name, answer=answer)
    except Exception as e:
        print(f"Error reading project {project_index} for user {user_id}: {e}")
        return None

def new_project(plan: Plan, user_level: int) -> Project|None:
    """
    生成一个新的项目，并确保其参考答案能通过自动生成的测试用例。

    Args:
        plan: 用户的学习计划。
        user_level: 用户当前的计划步骤索引。

    Returns:
        一个有效的 Project 对象，包含代码、Markdown 描述和通过测试的参考答案。
    """
    max_retries = 8 # 设置最大重试次数，防止无限循环
    for attempt in range(max_retries):
        print(f"Attempt {attempt + 1} to generate a valid project for level {user_level}...")
        # 1. 生成项目框架 (代码 + md)
        project_data = agent_project_maker(plan, user_level)
        
        # 2. 生成参考答案
        answer_code = agent_code_completer(project_data)
        project_data.answer=answer_code
        # 3. 生成测试用例

        test_cases = agent_testcase_generator(project_data)

        # 检查是否成功生成测试用例
        if not test_cases:
             print(f"Warning: Failed to generate test cases for attempt {attempt + 1}. Retrying...")
             continue # 跳过当前尝试，进行下一次重试

        # 4. 测试参考答案
        test_result = execute_and_test_python_code(code=answer_code, test_cases=test_cases)

        # 5. 检查测试结果
        if test_result and test_result.get('all_passed', True):
            print(f"Successfully generated and validated project for level {user_level}.")
            # 将验证通过的答案添加到 Project 对象
            project_data.answer = answer_code
            return project_data
        else:
            print(f"Warning: Generated project (attempt {attempt + 1}) failed validation. Retrying...")


    # 如果达到最大重试次数仍然失败
    print(f"Error: Failed to generate a valid project after {max_retries} attempts for level {user_level}.")
    return None